package tool.controller;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import tool.cfgformats.GraphBuilder;
import tool.model.CfgGenerator;
import tool.model.DU_Path;
import tool.model.cfg.EntryNode;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;


import guru.nidi.graphviz.engine.Format;
import guru.nidi.graphviz.model.Graph;



public class Controller implements Initializable {
	@FXML
	Pane imgPane;
	@FXML
	Pane duPathsPane;
	@FXML
	Pane defusePane;
	@FXML
    Circle close;
    @FXML
    Circle min;
    @FXML
    Circle max;
    @FXML
    TextArea textContent;
    @FXML
    TextArea duPathsResult;
    @FXML
    ImageView loadingImg;
    @FXML
    ImageView dotPng;
    @FXML
    Label filePathLabel;
    @FXML
    ComboBox<String> functionList;
    @FXML
    ComboBox<String> defNameList;
    @FXML
    TableView<NodeDefUse> tableView;

    public static String fileName = "";
	public static HashMap<String, ArrayList<EntryNode>> cfgs;
	public static Map<String, Map> map;
	
	public void defuseCalculate(MouseEvent event){
		imgPane.setVisible(false);
    	duPathsPane.setVisible(false);
    	defusePane.setVisible(true);
    	List<NodeDefUse> nodeDefUsesList = new ArrayList<>();
    	//allDefNodes
    	Map<String, ArrayList<String>> defMap =map.get("allDefNodes");
    	//allUseNodes
    	Map<String, ArrayList<String>> useMap =map.get("allUseNodes");
    	int count = 0;
    	for(String defName : defMap.keySet()){
    		count++;
    		NodeDefUse nodeDefUse = new NodeDefUse();
    		nodeDefUse.setId(count);
    		nodeDefUse.setVariable(defName);
    		//defNode
    		ArrayList<String> idList = defMap.get(defName);
    		String defList = "";
    		for (String string : idList) {
				defList = defList + string + ";";
			}
    		nodeDefUse.setDefList(defList);
    		//useNode
    		idList = useMap.get(defName);
    		String useList = "";
    		for (String string : idList) {
				useList = useList + string + ";";
			}
    		nodeDefUse.setUseList(useList);
    		
    		nodeDefUsesList.add(nodeDefUse);
    	}
    	tableView.setItems(FXCollections.observableList(nodeDefUsesList)); 
	}
	public void duPathCalculateBtn(MouseEvent event){
		//allPaths
        Map<String, ArrayList<ArrayList<String>>> pathMap =map.get("allPaths");
        //allDefNodes
    	Map<String, ArrayList<String>> defMap =map.get("allDefNodes");
    	//allUseNodes
    	Map<String, ArrayList<String>> useMap =map.get("allUseNodes");
		String funcName = functionList.getValue();
		String defName = defNameList.getValue();
		DU_Path duPath = new DU_Path();
		ArrayList<String> resultList = duPath.findDUs(pathMap, defMap, useMap, funcName, defName);
		String result = "";
    	for (String string : resultList) {
			result = result + string + "\n";
		}
    	duPathsResult.setText(result);
	}
	public void duPathsCalculate(MouseEvent event){
		imgPane.setVisible(false);
    	duPathsPane.setVisible(true);
    	defusePane.setVisible(false);
    	
    	functionList.getItems().clear();
    	defNameList.getItems().clear();
    	//allPaths
        Map<String, ArrayList<ArrayList<String>>> pathMap =map.get("allPaths");
        for(String funcName : pathMap.keySet()){
        	functionList.getItems().add(funcName);
    	}
        functionList.setValue("voidmain");
    	//allDefNodes
    	Map<String, ArrayList<String>> defMap =map.get("allDefNodes");
    	for(String defName : defMap.keySet()){
    		defNameList.getItems().add(defName);
    	}
    	defNameList.setValue("i");
    	
	}
	boolean analysisFlag = true;//需要重新计算
    //分析
    public void calculate(MouseEvent event){
    	imgPane.setVisible(true);
    	duPathsPane.setVisible(false);
    	defusePane.setVisible(false);
    	if(!textContent.getText().isEmpty() && analysisFlag){
    		loadingImg.setVisible(true);
    		StopThread st = new StopThread(); 
        	Thread t1 = new Thread(st);
            t1.setDaemon(true); 
        	t1.start();
    		
    	}	
  
    }
   
    
    public static String readFileContent(File file) {
        BufferedReader reader = null;
        StringBuffer sbf = new StringBuffer();
        try {
        	InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
            reader = new BufferedReader(isr);
            String tempStr;
            while ((tempStr = reader.readLine()) != null) {
                sbf.append(tempStr);
                sbf.append("\r\n");
            }
            reader.close();
            return sbf.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return sbf.toString();
    }
    
    public void openFile(MouseEvent event){
    	FileChooser fileChooser = new FileChooser();
    	FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("C files (*.c)", "*.c");
        fileChooser.getExtensionFilters().add(extFilter);
        Stage appStage = new Stage();
        File file = fileChooser.showOpenDialog(appStage);
        filePathLabel.setText(file.getAbsolutePath());
        fileName = file.getAbsolutePath();
        analysisFlag = true;//重新载入文件则需要重新计算
        String contentStr = readFileContent(file);
        if (!contentStr.isEmpty()){
        	textContent.setText(contentStr);
        }
    	
    }

    Point2D dragDistance = null;
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
    	System.out.println("初始化组件");
    	//窗体左上角提示框
        Tooltip tip=new Tooltip();
        tip.setText("关闭窗口");//设置Tooltip内容
        tip.setFont(new Font("Arial", 14));//设置Tooltip内容字体
        Tooltip.install(close, tip);
        Tooltip tip2=new Tooltip();
        tip2.setText("最小化");//设置Tooltip内容
        tip2.setFont(new Font("Arial", 14));//设置Tooltip内容字体
        Tooltip.install(min, tip2);
        Tooltip tip3=new Tooltip();
        tip3.setText("最大化");//设置Tooltip内容
        tip3.setFont(new Font("Arial", 14));//设置Tooltip内容字体
        Tooltip.install(max, tip3);
        Rectangle rec = new Rectangle(880, 650);
        rec.setArcHeight(50);
        rec.setArcWidth(50);
        rec.setX(0);
        //pane显示
        imgPane.setVisible(true);
        duPathsPane.setVisible(false);
        defusePane.setVisible(false);
        //图片拖拽
        dotPng.setClip(rec);
        final double w = dotPng.getFitWidth();
        final double h = dotPng.getFitHeight();
        final double max = Math.max(w, h);
        final int width = (int) (500 * w / max);
        final int heigth = (int) (500 * h / max);
        final double scale = 5;
        imgPane.heightProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            	dotPng.setY((newValue.doubleValue() - dotPng.getFitHeight()) / 2);
            }
 
        });
        imgPane.widthProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            	dotPng.setX((newValue.doubleValue() - dotPng.getFitWidth()) / 2);
            }
 
        });
        imgPane.addEventFilter(MouseDragEvent.MOUSE_PRESSED, event -> {
            dragDistance = new Point2D(event.getSceneX(), event.getSceneY());
            dragDistance = dragDistance.subtract(imgPane.localToScene(new Point2D(dotPng.getX(), dotPng.getY())));
 
        });
 
        imgPane.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, event -> {
            if (event.isPrimaryButtonDown()) {
            	Point2D px = new Point2D(event.getSceneX(), event.getSceneY());
                px = imgPane.sceneToLocal(px.subtract(dragDistance));
                dotPng.setX(px.getX());
                dotPng.setY(px.getY());
            }
        });
        imgPane.addEventFilter(ScrollEvent.SCROLL, event -> {
            double rate = 0;
            if (event.getDeltaY() > 0) {
                rate = 0.05;
            } else {
                rate = -0.05;
            }
            double newWidth = dotPng.getFitWidth() + w * rate;
            double newHeight = dotPng.getFitHeight() + h * rate;
            if (newWidth <= width  || newWidth > scale * width ) {
                return;
            }
            
            Point2D eventPoint = new Point2D(event.getSceneX(), event.getSceneY());
            Point2D imagePoint = imgPane.localToScene(new Point2D(dotPng.getX(), dotPng.getY()));
            Rectangle2D imageRect = new Rectangle2D(imagePoint.getX(), imagePoint.getY(), dotPng.getFitWidth(), dotPng.getFitHeight());
            Point2D ratePoint;
            Point2D eventPointDistance;
            if (newWidth > scale / 4 * width && imageRect.contains(eventPoint)) {
                ratePoint = eventPoint.subtract(imagePoint);
                ratePoint = new Point2D(ratePoint.getX() / dotPng.getFitWidth(), ratePoint.getY() / dotPng.getFitHeight());
                eventPointDistance = imgPane.sceneToLocal(eventPoint);
            } else {
                ratePoint = new Point2D(0.5, 0.5);
                eventPointDistance = new Point2D(imgPane.getWidth() / 2,
                		imgPane.getHeight() / 2);
            }
 
            dotPng.setX(eventPointDistance.getX() - newWidth * ratePoint.getX());
            dotPng.setY(eventPointDistance.getY() - newHeight * ratePoint.getY());
            dotPng.setFitWidth(newWidth);
            dotPng.setFitHeight(newHeight);
        });
        createTable();
        
    }
    private void createTable() {
    	
    	tableView.setMinWidth(800);
    	tableView.setMaxWidth(800);
    	tableView.setMaxHeight(400);
    	tableView.setMinHeight(400);
    	
    	tableView.setBackground(new Background(new BackgroundFill(javafx.scene.paint.Color.TRANSPARENT,null,null)));
    	TableColumn<NodeDefUse, Integer> num = new TableColumn<>("序号");
        num.setCellValueFactory(new PropertyValueFactory("id"));
        num.setMaxWidth(50);
    	TableColumn<NodeDefUse, String> nameCol0 = new TableColumn<>("变量名称");
        nameCol0.setCellValueFactory(new PropertyValueFactory("variable"));
        nameCol0.setMaxWidth(150);
        TableColumn<NodeDefUse, String> nameCol1 = new TableColumn<>("定义该变量节点");
        nameCol1.setCellValueFactory(new PropertyValueFactory("defList"));
        nameCol1.setStyle("-fx-text-fill:#2BD56F;-fx-align:CENTER;");
        nameCol1.setMaxWidth(300);
        TableColumn<NodeDefUse, String> nameCol2 = new TableColumn<>("使用该变量节点");
        nameCol2.setCellValueFactory(new PropertyValueFactory("useList"));
        nameCol2.setStyle("-fx-text-fill:#E61A1A;-fx-align:CENTER;");
        nameCol2.setMaxWidth(300);
        tableView.getColumns().addAll(num,nameCol0,nameCol1,nameCol2);
        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
       
    }
    /**** minimize ****/
    @FXML
    public void minclick(MouseEvent event) throws IOException {
        ((Stage)((Circle)event.getSource()).getScene().getWindow()).setIconified(true);
    }
    
    /**** maxnimize ****/
    @FXML
    public void maxclick(MouseEvent event) throws IOException {
    	((Stage)((Circle)event.getSource()).getScene().getWindow()).setMaximized(true);
    }

    /**** close screen ****/
    @FXML
    public void closeclick(MouseEvent event) throws IOException {
    	//driver.close();
        System.exit(0);

    }
  
    
    class StopThread implements Runnable
    {
        
        public void run()
        {
        	CfgGenerator cfgGenerator = new CfgGenerator();
            cfgs = cfgGenerator.generateCCfg(fileName);
            GraphBuilder graphBuilder = new GraphBuilder();
            List<EntryNode> cfgList = new ArrayList<>();
            for(ArrayList<EntryNode> list : cfgs.values()) {
                cfgList.addAll(list);
            }
            Map<String, Graph> graphs = graphBuilder.createGraphs(cfgList);
            Graph graph = graphBuilder.createGraph(cfgList);
            graphBuilder.exportGraph(graph, fileName, Format.PNG);
            DU_Path duPath = new DU_Path();
            map = duPath.createGraph(cfgList);
            
            if(loadingImg.isVisible())
            {
            	
            	Platform.runLater(new Runnable() {
            	    @Override
            	    public void run() {
            	    	
            	    	
            	    	try {
            	    		dotPng.setVisible(true);
                			Image imageForFile = new Image(new File(fileName+".png").toURI().toURL().toExternalForm());
                			dotPng.setImage(imageForFile);
                			dotPng.setFitWidth(imageForFile.getWidth());
                			dotPng.setFitHeight(imageForFile.getHeight());
                			
                    	} catch (MalformedURLException e) {
                			// TODO Auto-generated catch block
                			e.printStackTrace();
                		}

            	    	loadingImg.setVisible(false);
            	    	analysisFlag = false;//之后不再需要重新计算
            	    }
            	});
          
        	}	
               
            
        }
    }
}
